package org.bdware.sc;

import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bdware.sc.util.JsonUtil;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class ComponedContractResult {
    //    public static final String RecoveringStr = "";
    public static final String EXPIRED_REQ = "requestID expired";
    private static final Logger LOGGER = LogManager.getLogger(ComponedContractResult.class);
    private final int THRESHOLD; //  >=的结果可返回
    Map<String, ContractResult> resultMap = new HashMap<>(); // 只有状态是Success的返回结果会存在这个map中
    Set<String> problemNodes = new HashSet<>();

    // FAFH仅需要一半的一半一致即可?
    public ComponedContractResult(int c) {
        THRESHOLD = (int) Math.ceil((double) c / 2);
    }

    public synchronized void add(JsonObject obj) {
        // 节点isRecovering
        if (obj.has("status")
                && obj.get("status").getAsString().equals("Error")
                && obj.has("result")
                && obj.get("result").getAsString().equals("not fine")) {
            LOGGER.info("加入一个节点的执行结果是not infe!该节点不是正常运行状态!");
            String nodeID = obj.get("nodeID").getAsString();
            problemNodes.add(nodeID);
            LOGGER.info("问题节点中加入 " + nodeID.substring(0, 5));
            return;
        }

        // 节点下线，不用在此处恢复，心跳已经发现问题了
        if (obj.has("status") && obj.get("status").getAsString().equals("Error")) {
            LOGGER.info("加入一个节点的执行结果是Error!该节点下线!");
            return;
        }

        ContractResult cr2;
        try {

            cr2 = JsonUtil.fromJson(obj.get("data").getAsString(), ContractResult.class);
            if (cr2.status == ContractResult.Status.Error
                    && cr2.result.getAsString().equals(EXPIRED_REQ)) {
                return;
            }
        } catch (Exception e) {
            cr2 =
                    new ContractResult(
                            ContractResult.Status.Error, new JsonPrimitive("parse str failed:"));
            e.printStackTrace();
        }
        // if (cr2.status == ContractResult.Status.Error) { // 结果不是Success不参与最终结果计算
        //   String nodeID = obj.get("nodeID").getAsString();
        //  logger.info("problemsNodes中加入 " + nodeID);
        //  problemNodes.add(nodeID);
        //  return;
        // }
        //  if(cr2.status == ContractResult.Status.Exception){
        //    return;
        // }
        // cr2.executeTime.add(obj.get("executeTime").getAsLong());
        resultMap.put(obj.get("nodeID").getAsString(), cr2);
    }

    public ContractResult figureFinalResult() {
        ContractResult finalResult = null;

        Map<JsonElement, Integer> map = new ConcurrentHashMap<>(); // <结果，出现次数>
        Map<JsonElement, ContractResult> tempMap =
                new ConcurrentHashMap<>(); // key为C哦耨天然磁铁Result的result字段

        for (String nodeID : resultMap.keySet()) {
            ContractResult tempResult = resultMap.get(nodeID);
            if (tempResult.result == null) tempResult.result = JsonNull.INSTANCE;
            JsonElement str = tempResult.result;
            // 遍历各个节点的返回结果
            if (!map.containsKey(str)) {
                map.put(str, 1);
                tempMap.put(str, tempResult);
            } else {
                int temp = map.get(str);
                map.put(str, temp + 1);
            }
        }

        for (JsonElement str : map.keySet()) {
            if (map.get(str) >= THRESHOLD) {
                finalResult = tempMap.get(str);
                break;
            }
        }

        if (finalResult != null) {
            finalResult.size = map.get(finalResult.result);
            for (String nodeID : resultMap.keySet()) {
                ContractResult temp = resultMap.get(nodeID);
                if (!temp.result.equals(finalResult.result)
                        && temp.status == ContractResult.Status.Error) {
                    problemNodes.add(nodeID);
                }
            }
        } else {
            // 返回结果，无多于一半一致
            finalResult =
                    new ContractResult(
                            ContractResult.Status.Error, new JsonPrimitive("invalid result"));
            finalResult.analysis = JsonUtil.toPrettyJson(resultMap);
        }

        return finalResult;
    }

    public ContractResult mergeFinalResult() {
        LOGGER.info("mergeFinalResult");

        Map<JsonElement, Integer> map = new ConcurrentHashMap<>(); // <结果，出现次数>

        // JsonParser
        JsonObject allJo = new JsonObject();
        ContractResult contractResult = new ContractResult(ContractResult.Status.Success, allJo);
        // TODO @bitvincent
        for (String nodeID : resultMap.keySet()) {
            // 取出
            ContractResult tempResult = resultMap.get(nodeID); // 取出结果
            if (tempResult != null) {
                allJo.add(nodeID, tempResult.result);
            }
            // 合并结果
        }
        return contractResult;
    }

    public Set<String> getProblemNodes() {
        return problemNodes;
    }
}
